/**
 * Copyright 2011 The nanojson Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.nulldev.util.languages.json.v2;

import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

import com.nulldev.util.data.Arrays.arrays.FastLists;

/**
 * Extends a {@link List} with helper methods to determine the underlying JSON
 * type of the list element.
 */
public class JsonArray implements List<Object> {
	private final List<Object> list;

	/**
	 * Creates an empty {@link JsonArray} with the default capacity.
	 */
	public JsonArray() {
		this.list = FastLists.list();
	}

	/**
	 * Creates an empty {@link JsonArray} with the default initial capacity.
	 */
	public JsonArray(int initialCapacity) {
		this.list = FastLists.list(initialCapacity);
	}

	/**
	 * Creates an empty {@link JsonArray} from the given collection of objects.
	 */
	public JsonArray(Collection<? extends Object> collection) {
		this.list = FastLists.list(collection);
	}

	/**
	 * Creates a {@link JsonArray} from an array of contents.
	 */
	public static JsonArray from(Object... contents) {
		JsonArray array = new JsonArray(Arrays.asList(contents));
		return array;
	}

	/**
	 * Creates a {@link JsonBuilder} for a {@link JsonArray}.
	 */
	public static JsonBuilder<JsonArray> builder() {
		return new JsonBuilder<JsonArray>(new JsonArray());
	}

	/**
	 * Returns the underlying object at the given index, or null if it does not
	 * exist.
	 */
	public Object get(int key) {
		return key < size() ? list.get(key) : null;
	}

	/**
	 * Returns the {@link JsonArray} at the given index, or null if it does not
	 * exist or is the wrong type.
	 */
	public JsonArray getArray(int key) {
		return getArray(key, null);
	}

	/**
	 * Returns the {@link JsonArray} at the given index, or the default if it does
	 * not exist or is the wrong type.
	 */
	public JsonArray getArray(int key, JsonArray default_) {
		Object o = get(key);
		if (o instanceof JsonArray)
			return (JsonArray) get(key);
		return default_;
	}

	/**
	 * Returns the {@link Boolean} at the given index, or false if it does not exist
	 * or is the wrong type.
	 */
	public boolean getBoolean(int key) {
		return getBoolean(key, false);
	}

	/**
	 * Returns the {@link Boolean} at the given index, or the default if it does not
	 * exist or is the wrong type.
	 */
	public boolean getBoolean(int key, Boolean default_) {
		Object o = get(key);
		if (o instanceof Boolean)
			return (Boolean) o;
		return default_;
	}

	/**
	 * Returns the {@link Double} at the given index, or 0.0 if it does not exist or
	 * is the wrong type.
	 */
	public double getDouble(int key) {
		return getDouble(key, 0);
	}

	/**
	 * Returns the {@link Double} at the given index, or the default if it does not
	 * exist or is the wrong type.
	 */
	public double getDouble(int key, double default_) {
		Object o = get(key);
		if (o instanceof Number)
			return ((Number) o).doubleValue();
		return default_;
	}

	/**
	 * Returns the {@link Float} at the given index, or 0.0f if it does not exist or
	 * is the wrong type.
	 */
	public float getFloat(int key) {
		return getFloat(key, 0);
	}

	/**
	 * Returns the {@link Float} at the given index, or the default if it does not
	 * exist or is the wrong type.
	 */
	public float getFloat(int key, float default_) {
		Object o = get(key);
		if (o instanceof Number)
			return ((Number) o).floatValue();
		return default_;
	}

	/**
	 * Returns the {@link Integer} at the given index, or 0 if it does not exist or
	 * is the wrong type.
	 */
	public int getInt(int key) {
		return getInt(key, 0);
	}

	/**
	 * Returns the {@link Integer} at the given index, or the default if it does not
	 * exist or is the wrong type.
	 */
	public int getInt(int key, int default_) {
		Object o = get(key);
		if (o instanceof Number)
			return ((Number) o).intValue();
		return default_;
	}

	/**
	 * Returns the {@link Long} at the given index, or 0 if it does not exist or is
	 * the wrong type.
	 */
	public long getLong(int key) {
		return getLong(key, 0);
	}

	/**
	 * Returns the {@link Long} at the given index, or the default if it does not
	 * exist or is the wrong type.
	 */
	public long getLong(int key, long default_) {
		Object o = get(key);
		if (o instanceof Number)
			return ((Number) o).longValue();
		return default_;
	}

	/**
	 * Returns the {@link Number} at the given index, or null if it does not exist
	 * or is the wrong type.
	 */
	public Number getNumber(int key) {
		return getNumber(key, null);
	}

	/**
	 * Returns the {@link Number} at the given index, or the default if it does not
	 * exist or is the wrong type.
	 */
	public Number getNumber(int key, Number default_) {
		Object o = get(key);
		if (o instanceof Number)
			return (Number) o;
		return default_;
	}

	/**
	 * Returns the {@link JsonObject} at the given index, or null if it does not
	 * exist or is the wrong type.
	 */
	public JsonObject getObject(int key) {
		return getObject(key, null);
	}

	/**
	 * Returns the {@link JsonObject} at the given index, or the default if it does
	 * not exist or is the wrong type.
	 */
	public JsonObject getObject(int key, JsonObject default_) {
		Object o = get(key);
		if (o instanceof JsonObject)
			return (JsonObject) get(key);
		return default_;
	}

	/**
	 * Returns the {@link String} at the given index, or null if it does not exist
	 * or is the wrong type.
	 */
	public String getString(int key) {
		return getString(key, null);
	}

	/**
	 * Returns the {@link String} at the given index, or the default if it does not
	 * exist or is the wrong type.
	 */
	public String getString(int key, String default_) {
		Object o = get(key);
		if (o instanceof String)
			return (String) get(key);
		return default_;
	}

	/**
	 * Returns true if the array has an element at that index (even if that element
	 * is null).
	 */
	public boolean has(int key) {
		return key < size();
	}

	/**
	 * Returns true if the array has a boolean element at that index.
	 */
	public boolean isBoolean(int key) {
		return get(key) instanceof Boolean;
	}

	/**
	 * Returns true if the array has a null element at that index.
	 */
	public boolean isNull(int key) {
		return key < size() && get(key) == null;
	}

	/**
	 * Returns true if the array has a number element at that index.
	 */
	public boolean isNumber(int key) {
		return get(key) instanceof Number;
	}

	/**
	 * Returns true if the array has a string element at that index.
	 */
	public boolean isString(int key) {
		return get(key) instanceof String;
	}

	@Override
	public int size() {
		return this.list.size();
	}

	@Override
	public boolean isEmpty() {
		return this.list.isEmpty();
	}

	@Override
	public boolean contains(final Object o) {
		return this.list.contains(o);
	}

	@Override
	public Iterator<Object> iterator() {
		return this.list.iterator();
	}

	@Override
	public Object[] toArray() {
		return this.list.toArray();
	}

	@Override
	public <T> T[] toArray(final T[] a) {
		return this.list.toArray(a);
	}

	@Override
	public boolean add(final Object e) {
		return this.list.add(e);
	}

	@Override
	public boolean remove(final Object o) {
		return this.list.remove(o);
	}

	@Override
	public boolean containsAll(final Collection<?> c) {
		return this.list.containsAll(c);
	}

	@Override
	public boolean addAll(final Collection<? extends Object> c) {
		return this.list.addAll(c);
	}

	@Override
	public boolean addAll(final int index, final Collection<? extends Object> c) {
		return this.list.addAll(index, c);

	}

	@Override
	public boolean removeAll(final Collection<?> c) {
		return this.list.removeAll(c);
	}

	@Override
	public boolean retainAll(final Collection<?> c) {
		return this.list.retainAll(c);
	}

	@Override
	public void clear() {
		this.list.clear();
	}

	@Override
	public Object set(final int index, final Object element) {
		return this.list.set(index, element);
	}

	@Override
	public void add(final int index, final Object element) {
		this.list.add(index, element);
	}

	@Override
	public Object remove(final int index) {
		return this.list.remove(index);
	}

	@Override
	public int indexOf(final Object o) {
		return this.list.indexOf(o);
	}

	@Override
	public int lastIndexOf(final Object o) {
		return this.list.lastIndexOf(o);
	}

	@Override
	public ListIterator<Object> listIterator() {
		return this.list.listIterator();
	}

	@Override
	public ListIterator<Object> listIterator(final int index) {
		return this.list.listIterator(index);
	}

	@Override
	public List<Object> subList(final int fromIndex, final int toIndex) {
		return this.list.subList(fromIndex, toIndex);
	}
}
